Web 應用程式能做到很多強大的功能,但也因此讓程式變得愈來愈複雜,靠著瀏覽器幫我們處理大量的資料和複雜的計算。雖然瀏覽器很強大,但當每個分頁的應用程式都很複雜時,就容易產生效能問題,甚至讓瀏覽器或分頁當機。想要解決這類的問題,我們可以使用 Web Workers API 讓應用程式在後台執行緒執行任務,從而保持主線的暢通,走出屬於自己的快速通道 XD。
Web Workers API 可以讓 JavaScript 在獨立的執行緒中工作,而不會影響主執行緒的效能,以提升 Web 應用的回應速度和使用者體驗。
Web Workers 提供了在獨立執行緒中執行 JavaScript 的能力,以避免阻塞主執行緒。Web Workers 分為三種主要類型:Dedicated Workers、Shared Workers 和 Service Workers,每種類型都有特定的用途和特性,未來可以視需求選擇要使用的類型。
Dedicated 的中文是專門、專用的意思,表示他只能給一個主執行緒使用,而他也是最常見的 Web Worker 類型,用於執行計算複雜或需要長時間處理的任務,例如資料處理、圖片處理,或複雜的演算法 ... 等等,所以很適合在單個頁面中使用,進行獨立的後台任務處理。
以下是一個簡單的範例,首先在 main.js
建立一個 Worker 實例,當 worker postMessage
時,可以用 onmessage
監聽接收,有一點 web socket 的感覺。
// main.js
const worker = new Worker('worker.js');
worker.onmessage = function (e) {
console.log('主執行緒收到結果:', e.data);
};
worker.postMessage(10); // 發送消息到 Worker
接著建立 worker 檔案,透過 postMessage
將結果傳回給主執行緒
// worker.js
self.onmessage = function (e) {
const result = e.data * 3.1415926; // 假設任務是做一個複雜的計算
self.postMessage(result);
};
如果要終止 worker 服務,可以使用 terminate
方法:
worker.terminate();
和 web socket 相同,我們也能使用 onerror
取得錯誤處理函式,不管是在 worker 腳本或是主執行緒都能使用:
// main.js
worker.onerror = function (e) {
console.log('Error in Worker:', e.message);
};
// worker.js
self.onerror = function (e) {
console.log('Error in Worker:', e.message);
};
Dedicated Workers 的特色是:
main.js
)使用,不能跨頁面共享任務透過以上特色不難發現,如果想要跨頁面共享任務,就必須用 Shared Workers。
Shared Workers 可以讓多個主執行緒 (多個頁面或 iframe) 共享同一個 Worker 實例,可以同時被多個主執行緒存取使用,如果是同個域名下的多頁面或應用,需要共享後台任務處理的話,就非常適合使用 Shared Workers。
在 main.js
連接到 Shared Workers
// main.js
const worker = new SharedWorker('shared-worker.js');
worker.port.onmessage = function (e) {
console.log('主執行緒收到結果:', e.data);
};
worker.port.start();
worker.port.postMessage({ source: 'main', message: 'Hello MUKI in main.js' });
在 event.js
也連接 Shared Workers
// event.js
const worker = new SharedWorker('shared-worker.js');
worker.port.onmessage = function (e) {
console.log('主執行緒收到結果:', e.data);
};
worker.port.start();
worker.port.postMessage({ source: 'event', message: 'Hello MUKI in event.js' });
在發送消息時,跟 Dedicated Workers 的差別在於 Shared Workers 會使用 port
來傳遞消息
接著建立 shared-worker.js
,來處理 Shared Workers 邏輯。
// shared-worker.js
let connections = 0;
self.onconnect = function (e) {
const port = e.ports[0];
connections++;
port.postMessage(`Worker 服務已連接,總連接數量:${connections}`);
port.onmessage = function (event) {
// 處理從各個連接發送過來的消息
const message = `來源:${event.data.source},內容:${event.data.message}`;
// 將消息發送回所有連接的 port
self.clients.matchAll().then(clients => {
clients.forEach(client => {
client.postMessage(message);
});
});
};
port.onclose = function () {
connections--;
};
};
特別注意的是,Shared Worker 必須遵守瀏覽器的同源政策,也就是必須在同一個域名和同一個 port 才能互相連接,共享同一個 Shared Worker 實例。
如果你有印象,會發現我已在 Web Notifications API 搭配 Service Workers 發送通知 介紹過 Service Workers 了。Service Workers 的特性就是處理網路的請求和快取資源,他們會在後台獨立運作,不會被頁面的生命週期影響,所以能在不開啟網頁,也就是應用離線的情況下繼續提供服務。
首先一樣要註冊 Service Workers:
// main.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js').then(function (registration) {
console.log('註冊成功', registration);
}).catch(function (error) {
console.log('註冊失敗', error);
});
}
在 service-worker.js
中處理各種事件,例如安裝 (install
),啟用 (activate
),以及網路請求 (fetch
):
self.addEventListener('install', function (event) {
console.log('Service Worker 已安裝');
});
self.addEventListener('activate', function (event) {
console.log('Service Worker 已啟用');
});
self.addEventListener('fetch', function (event) {
console.log('Fetching:', event.request.url);
event.respondWith(fetch(event.request).then(function (response) {
return response;
}).catch(function () {
return new Response('網路請求失敗');
}));
});
只要瀏覽器在有 install 且有 activate 的 Service Worker 作用域內做了任何的網路請求,fetch
事件就會自動觸發,觸發方式包括但不限於:
fetch
事件fetch
事件fetch
或 XMLHttpRequest
也會觸發 fetch
事件。Web Workers API 的三種服務,都可以幫助我們在執行緒塞車的時候,多出一條小路給後面的車子(X)通過,以拿到分流的目的。
希望這篇文章能讓大家更了解 Web Workers API,有任何問題也歡迎留言討論。